package com.cqrt.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.cqrt.constant.RTErrorCodeEnum;
import com.cqrt.dto.PlanActiveCompareDTO;
import com.cqrt.dto.PlanDeliveryCompleteInfoDTO;
import com.cqrt.entity.RTPreConstraintDO;
import com.cqrt.mapper.ProjectLinkMapper;
import com.cqrt.mapper.RTProjectMapper;
import com.cqrt.service.ProjectLinkService;
import com.cqrt.service.RTProjectService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hustcad.plm.pdm.common.model.dto.search.SearchAttributeDTO;
import com.hustcad.plm.pdm.common.model.dto.search.SearchAttributeGroupDTO;
import com.hustcad.plm.pdm.common.model.dto.search.SearchTypeDTO;
import com.hustcad.plm.pdm.common.model.vo.search.SearchAttributeVO;
import com.hustcad.plm.pdm.common.util.CommonUtil;
import com.hustcad.plm.pdm.container.model.dto.search.ProjectEntityParamDTO;
import com.hustcad.plm.pdm.container.model.vo.search.ProjectEntityVO;
import com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum;
import com.hustcad.plm.pdm.core.util.Tools;
import com.hustcad.plm.pdm.iba.model.dto.IBASaveParamDTO;
import com.hustcad.plm.pdm.iba.model.dto.SaveObjIBADTO;
import com.hustcad.plm.pdm.iba.service.TyplmAttributeValueService;
import com.hustcad.plm.pdm.iba.service.TyplmCommonAttributeService;
import com.hustcad.plm.pdm.type.model.vo.TypeVueVO;
import com.hustcad.plm.pdm.type.service.TyplmTypeService;
import com.hustcad.plm.rpm.constant.ProjectMngConstant;
import com.hustcad.plm.rpm.entityconvert.BaseConvert;
import com.hustcad.plm.rpm.model.dto.active.PlusProjectDTO;
import com.hustcad.plm.rpm.model.dto.active.PlusTaskDTO;
import com.hustcad.plm.rpm.model.dto.active.TyppmActiveTreeDTO;
import com.hustcad.plm.rpm.model.dto.issue.DeleteIssueLinkDTO;
import com.hustcad.plm.rpm.model.dto.outrule.TyppmOutRuleDTO;
import com.hustcad.plm.rpm.model.dto.remote.RemoteFolderSearchObjectList;
import com.hustcad.plm.rpm.model.dto.remote.RemoteObjDTO;
import com.hustcad.plm.rpm.model.dto.remote.TyppmChangeIssueLinkDTO;
import com.hustcad.plm.rpm.model.entity.plan.TyppmBaselinePlan;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlan;
import com.hustcad.plm.rpm.model.entity.remote.TyppmRemoteObject;
import com.hustcad.plm.rpm.model.vo.base.EntityBaseVO;
import com.hustcad.plm.rpm.project.service.TyppmProjectService;
import com.hustcad.plm.rpm.service.issue.TyppmProjectChangeIssueLinkService;
import com.hustcad.plm.rpm.service.outrule.TyppmOutRuleService;
import com.hustcad.plm.rpm.service.plan.TyppmBaselinePlanActivityService;
import com.hustcad.plm.rpm.service.plan.TyppmBaselinePlanService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanActiveService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanService;
import com.hustcad.plm.rpm.service.remote.TyppmRemoteObjectIdService;
import com.hustcad.plm.rpm.service.remoterequest.ThirdRemoteRequestService;
import com.ty.basic.constant.TableTypeConstrant;
import com.ty.basic.dto.constraint.IbaDTO;
import com.ty.basic.entity.CTyEntityBaseDO;
import com.ty.basic.entity.IdentifierEntity;
import com.ty.ppm.api.service.issue.ProjectChangeIssueLinkService;
import com.ty.ppm.api.service.plan.BaselinePlanActivityService;
import com.ty.ppm.api.service.plan.BaselinePlanService;
import com.ty.ppm.model.dto.remote.ChangeIssueLinkDTO;
import com.ty.ppm.service.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import oracle.sql.CLOB;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.Reader;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class ProjectLinkServiceImpl implements ProjectLinkService {
    @Resource
    private TyppmProjectService typpmProjectService;
    @Resource
    private TyplmTypeService typlmTypeService;
    @Resource
    private ProjectLinkMapper projectLinkMapper;
    @Resource
    private TyppmRemoteObjectIdService typpmRemoteObjectIdService;
    @Resource
    private RTProjectMapper rtProjectMapper;
    @Resource
    private ThirdRemoteRequestService thirdRemoteRequestService;
    @Resource
    private BaseConvert baseConvert;
    @Resource
    private ProjectChangeIssueLinkService projectChangeIssueLinkService;
    @Resource
    private TyppmProjectChangeIssueLinkService typpmProjectChangeIssueLinkService;
    @Resource
    private TyppmOutRuleService typpmOutRuleService;
    @Resource
    private TyppmPlanService planService;
    @Resource
    private TyppmPlanActiveService planActiveService;
    @Resource
    private TyppmBaselinePlanService baselinePlanService;
    @Resource
    private TyppmBaselinePlanActivityService baselinePlanActivityService;
    @Resource
    private TyplmAttributeValueService typlmAttributeValueService;
    @Resource
    private TyppmPlanService typpmPlanService;

    @Override
    public PageInfo<ProjectEntityVO> searchAllProject(String keyword,String except, int page, int pageSize) {
        ProjectEntityParamDTO params = new ProjectEntityParamDTO();
        List<String> typeList = Arrays.asList(TableTypeConstrant.TY_PROJECT_TYPE, TableTypeConstrant.TY_PLAN_OTYPE);
        List<TypeVueVO> typeVueVOS = typlmTypeService.queryTypeByNameList(typeList);
        List<SearchTypeDTO> searchTypeVOList = new ArrayList<>();
        SearchTypeDTO projectTypeDTO = new SearchTypeDTO();
        for (TypeVueVO typeVueVO : typeVueVOS) {
            SearchTypeDTO searchTypeDTO = new SearchTypeDTO();
            searchTypeDTO.setTypeInthid(typeVueVO.getInthid());
            searchTypeDTO.setTypeOid(typeVueVO.getOid());
            searchTypeVOList.add(searchTypeDTO);
            if(TableTypeConstrant.TY_PROJECT_TYPE.equals(typeVueVO.getInthid())){
                projectTypeDTO.setTypeInthid(typeVueVO.getInthid());
                projectTypeDTO.setTypeOid(typeVueVO.getOid());
            }
        }

        String ibaAttrOid = projectLinkMapper.selectIbaAttrOidByName("ExecutionStatus");
        List<SearchAttributeGroupDTO> conditionGroups = new ArrayList<>();
        SearchAttributeGroupDTO conditionGroup = new SearchAttributeGroupDTO();
        conditionGroup.setConditionJoint("or");
        SearchAttributeDTO attributeDTO1 = new SearchAttributeDTO();
        attributeDTO1.setPropertyName("ExecutionStatus");
        attributeDTO1.setPropertyValue("执行中");
        attributeDTO1.setSymbol("=");
        attributeDTO1.setPropertyType("String");
        attributeDTO1.setIbaDefOid(new BigInteger(ibaAttrOid));
        attributeDTO1.setIbaDefOtype(TableTypeConstrant.TY_STRINGDEF_OTYPE);
        attributeDTO1.setTypeList(Collections.singletonList(projectTypeDTO));

        SearchAttributeDTO attributeDTO2 = new SearchAttributeDTO();
        attributeDTO2.setPropertyName("ExecutionStatus");
        attributeDTO2.setPropertyValue("正常");
        attributeDTO2.setSymbol("=");
        attributeDTO2.setPropertyType("String");
        attributeDTO2.setIbaDefOid(new BigInteger(ibaAttrOid));
        attributeDTO2.setIbaDefOtype(TableTypeConstrant.TY_STRINGDEF_OTYPE);
        attributeDTO2.setTypeList(Collections.singletonList(projectTypeDTO));

        List<SearchAttributeDTO> attributeDTOS = new ArrayList<>();
//        attributeDTOS.add(attributeDTO1);
        attributeDTOS.add(attributeDTO2);
        conditionGroup.setConditions(attributeDTOS);
        conditionGroups.add(conditionGroup);
        params.setKeyword(keyword);
        params.setConditionGroups(conditionGroups);
        params.setConditionJoint("and");
        params.setTypeList(searchTypeVOList);
        params.setPageNum(page);
        params.setPageSize(pageSize);
        params.setNeedIBA(true);
        params.setNeedThumbnailFile(true);
        params.setNeedCount(true);
        params.setNoExtProperty(false);
        params.setShowAllViewVersion(false);
        params.setLimitContext(false);
        params.setNeedEmptyCondition(false);
        params.setNeedLatest(false);
        params.setNeedSort(true);
        return this.typpmProjectService.queryProjectAdvancedSearch(params);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createProjectPlanLink(JSONObject params) {

    }

    @Override
    public PageInfo<RTPreConstraintDO> selectProjectPlanLinkByProjectId(String projectOid , int page, int pageSize) {
        PageHelper.startPage(page, pageSize);
        List<RTPreConstraintDO> rtPreConstraintDOList = projectLinkMapper.selectPreConstraintById(projectOid);
        return new PageInfo<>(rtPreConstraintDOList);
    }

    @Override
    public JSONObject queryChangeIssueListByParamsFromProject(EntityBaseVO baseVO, JSONObject jsonParams) {
        if (Objects.isNull(baseVO)) {
            throw SystemErrorCodeEnum.ARGUMENT_NONE.message("baseVO");
        } else if (Objects.isNull(jsonParams)) {
            throw SystemErrorCodeEnum.ARGUMENT_NONE.message("jsonParams");
        } else if(Objects.isNull(jsonParams.getString("isQueryRisk"))){
            throw SystemErrorCodeEnum.ARGUMENT_NONE.message("isQueryRisk");
        }else {
            String isQueryRisk = jsonParams.getString("isQueryRisk");
            Map<String, Object> linkParams = new HashMap<>();
            linkParams.put("projectOid", baseVO.getOid());
            linkParams.put("projectOtype", baseVO.getOtype());
            linkParams.put("markfordelete", 0);
            List<TyppmChangeIssueLinkDTO> issueLinkList = this.listChangeIssueLinkDTOByProject(linkParams);
            if (CollUtil.isEmpty(issueLinkList)) {
                return (JSONObject) JSON.toJSON(new PageInfo<>(Lists.newArrayList()));
            } else {
                Map<String, List<TyppmChangeIssueLinkDTO>> remoteObjectGroup = issueLinkList.stream().collect(
                        Collectors.groupingBy(TyppmChangeIssueLinkDTO::getLinkBOid));
                List<BigInteger> integerList = issueLinkList.stream().map((item) -> Tools.toBigInteger(item.getLinkBOid())).collect(Collectors.toList());
                Map<String, Object> remoteParams = new HashMap<>();
                remoteParams.put("oid", new ArrayList<>(integerList));
                remoteParams.put("oidList", new ArrayList<>(integerList));
                remoteParams.put("markForDelete", 0);
                List<TyppmRemoteObject> remoteObjectList = this.typpmRemoteObjectIdService.simpleQueryListByParamsEx(remoteParams);
                if (CollUtil.isEmpty(remoteObjectList)) {
                    return (JSONObject)JSON.toJSON(new PageInfo<>(Lists.newArrayList()));
                } else {
                    this.checkChangeIssueIsExistAndDelete(baseVO, remoteObjectList, remoteObjectGroup);
                    return queryChangeIssueListByParamsFromProject(baseVO, jsonParams, remoteObjectList, remoteObjectGroup, isQueryRisk);
                }
            }
        }
    }

    public JSONObject queryChangeIssueListByParamsFromProject(EntityBaseVO baseVO, JSONObject jsonParams, List<TyppmRemoteObject> remoteObjectList, Map<String, List<TyppmChangeIssueLinkDTO>> remoteObjectGroup, String isQueryRisk) {
        this.getRemoteChangeIssueParams(baseVO, remoteObjectList, jsonParams, isQueryRisk);
        JSONObject responseData = this.thirdRemoteRequestService.getFolderObjectByFilter(jsonParams);
        List<JSONObject> result = new ArrayList<>();
        if (ObjectUtils.isEmpty(responseData)) {
            return responseData;
        } else {
            JSONArray responseListJSONArray = responseData.getJSONArray("list");
            if (ObjectUtils.isEmpty(responseListJSONArray)) {
                return responseData;
            } else {
                Map<String, List<TyppmRemoteObject>> remoteMapByBranch = remoteObjectList.stream().collect(Collectors.groupingBy(TyppmRemoteObject::getRemoteObjectId));

                for (JSONObject jsonObject : responseListJSONArray.toJavaList(JSONObject.class)) {
                    String branchId = jsonObject.getString("branchid");
                    List<TyppmRemoteObject> remoteBranchList = remoteMapByBranch.get(branchId);
                    List<TyppmChangeIssueLinkDTO> tempList = new ArrayList<>();
                    if (!ObjectUtils.isEmpty(remoteBranchList)) {
                        remoteBranchList.forEach((item) -> {
                            tempList.addAll(remoteObjectGroup.get(item.getOid()));
                        });
                        result.addAll(this.mergeRemoteLinkAndIssue(jsonObject, tempList));
                    }
                }

                responseData.put("list", result);
                dealResult(responseData, isQueryRisk);
                return responseData;
            }
        }
    }

    public void dealResult(JSONObject jsonObject, String isQueryRisk) {

        JSONArray list = jsonObject.getJSONArray("list");

        if (CollUtil.isNotEmpty(list)) {
            List<String> oids = list.stream().filter(item -> item instanceof JSONObject).map(item -> ((JSONObject) item).getString("oid")).filter(Objects::nonNull).collect(Collectors.toList());
            List<JSONObject> objectList = rtProjectMapper.selectIssueFormData(oids);
            Map<String, Object> formDataMap = objectList.stream().collect(
                    Collectors.toMap(item -> item.getString("REFOBJBASEOID"), item -> item.get("formData"),
                                     (existing, replacement) -> replacement));
            for (int i = 0; i < list.size(); i++) {
                JSONObject issueData = list.getJSONObject(i);
                Map<String, Object> map = new HashMap<>();
                String oid = issueData.getString("oid");
                if(!formDataMap.containsKey(oid)){
                    continue;
                }
                JSONObject formData = clobToJSONObject((CLOB)formDataMap.get(oid));
                if ("1".equals(isQueryRisk)) {
                    dealRiskFromData(map, formData);
                }else {
                    dealIssueFromData(map,formData);

                }
                if (CollUtil.isNotEmpty(map)) {
                    issueData.putAll(map);
                }
            }
        }
    }

    public static JSONObject clobToJSONObject(CLOB clob) {
        if (clob == null) {
            return null;
        }
        try (Reader reader = clob.getCharacterStream();
             BufferedReader br = new BufferedReader(reader)) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            return JSONObject.parseObject(sb.toString());
        } catch (Exception e) {
            log.error("CLOB转换失败", e);
            return null;
        }
    }

    private void dealRiskFromData(Map<String, Object> map, JSONObject fromData) {
        map.put("ProjectNumber", fromData.containsKey("ProjectNumber") ? fromData.getString("ProjectNumber") : "");
        map.put("ProjectName", fromData.containsKey("ProjectName") ? fromData.getString("ProjectName") : "");
        map.put("ProjectLeader", fromData.containsKey("ProjectLeader") ? fromData.getString("ProjectLeader") : "");
        map.put("RiskEvent", fromData.containsKey("RiskEvent") ? fromData.getString("RiskEvent") : "");
        map.put("Type", fromData.containsKey("Type") ? fromData.getString("Type") : "");
        map.put("Influence", fromData.containsKey("Influence") ? fromData.getString("Influence") : "");
        map.put("OccurrenceProcess", fromData.containsKey("OccurrenceProcess") ? fromData.getString("OccurrenceProcess") : "");
        map.put("TimeOccurrence", fromData.containsKey("TimeOccurrence") ? fromData.getString("TimeOccurrence") : "");
        map.put("ProbabilityOccurrence", fromData.containsKey("ProbabilityOccurrence") ? fromData.getString("ProbabilityOccurrence") : "");
        map.put("SignsOccurrence", fromData.containsKey("SignsOccurrence") ? fromData.getString("SignsOccurrence") : "");
        map.put("RiskResponseMeasures",  fromData.containsKey("RiskResponseMeasures") ? fromData.getString("RiskResponseMeasures") : "");
        map.put("PersonCharge", fromData.containsKey("PersonCharge") ? fromData.getString("PersonCharge") : "");
        map.put("PlannedCompletionTime", fromData.containsKey("PlannedCompletionTime") ? fromData.getString("PlannedCompletionTime") : "");
        map.put("Remarks", fromData.containsKey("Remarks") ? fromData.getString("Remarks") : "");
    }

    private void dealIssueFromData(Map<String, Object> map, JSONObject fromData) {
        map.put("Type", fromData.containsKey("Type") ? fromData.getString("Type") : "");
        map.put("ProjectNo", fromData.containsKey("ProjectNo") ? fromData.getString("ProjectNo") : "");
        map.put("TerminalModel", fromData.containsKey("TerminalModel") ? fromData.getString("TerminalModel") : "");//终端机型
        map.put("PowerModel", fromData.containsKey("PowerModel") ? fromData.getString("PowerModel") : "");//动力型号
        map.put("ComponentName", fromData.containsKey("ComponentName") ? fromData.getString("ComponentName") : "");//零部件名称
        map.put("PatternCode", fromData.containsKey("PatternCode") ? fromData.getString("PatternCode") : "");//图样代号
        map.put("OccurrenceDate", fromData.containsKey("OccurrenceDate") ? fromData.getString("OccurrenceDate") : "");//设置发生日期
        map.put("OccurrenceStage", fromData.containsKey("OccurrenceStage") ? fromData.getString("OccurrenceStage") : "");//发生阶段
        map.put("OccurrenceEnvironment", fromData.containsKey("OccurrenceEnvironment") ? fromData.getString("OccurrenceEnvironment") : "");//发生环境
        map.put("Importance", fromData.containsKey("Importance") ? fromData.getString("Importance") : ""); //重要度
        map.put("Proposer", fromData.containsKey("Proposer") ? fromData.getString("Proposer") : ""); //提出人
        map.put("VIN", fromData.containsKey("VIN") ? fromData.getString("VIN") : "");  //vin
        map.put("PhenomenonDescription", fromData.containsKey("PhenomenonDescription") ? fromData.getString("PhenomenonDescription") : ""); //不良现象描述
        map.put("AnalysisDescription", fromData.containsKey("AnalysisDescription") ? fromData.getString("AnalysisDescription") : "");  //原因解析描述
        map.put("TentativeCountermeasures", fromData.containsKey("TentativeCountermeasures") ? fromData.getString("TentativeCountermeasures") : "");//对策（暂定对策）
        map.put("SourceImprovement", fromData.containsKey("SourceImprovement") ? fromData.getString("SourceImprovement") : ""); //源流改善
        map.put("LongCountermeasures", fromData.containsKey("LongCountermeasures") ? fromData.getString("LongCountermeasures") : "");  //对策（恒久对策）
        map.put("Supplier", fromData.containsKey("Supplier") ? fromData.getString("Supplier") : "");//供应商
        map.put("ProblemType", fromData.containsKey("ProblemType") ? fromData.getString("ProblemType") : ""); //问题类型
        map.put("ClosingStage", fromData.containsKey("ClosingStage") ? fromData.getString("ClosingStage") : "");  //关闭阶段
        map.put("PersonResponsible", fromData.containsKey("PersonResponsible") ? fromData.getString("PersonResponsible") : "");//承接人
        map.put("FeedbackDepartment", fromData.containsKey("FeedbackDepartment") ? fromData.getString("FeedbackDepartment") : "");  //反馈部门
        map.put("FeedbackPerson", fromData.containsKey("FeedbackPerson") ? fromData.getString("FeedbackPerson") : ""); //反馈人
        map.put("PlannedDate", fromData.containsKey("PlannedDate") ? fromData.getString("PlannedDate") : ""); //计划完成日期
        map.put("ConfirmationMethod", fromData.containsKey("ConfirmationMethod") ? fromData.getString("ConfirmationMethod") : ""); //确认方式
        map.put("ProgressConfirmation", fromData.containsKey("ProgressConfirmation") ? fromData.getString("ProgressConfirmation") : "");  //确认进度
        map.put("IsClose", fromData.containsKey("IsClose") ? fromData.getString("IsClose") : "");  //是否关闭
        map.put("ConfirmDate", fromData.containsKey("ConfirmDate") ? fromData.getString("ConfirmDate") : ""); //确认日期
        map.put("ConfirmationPerson", fromData.containsKey("ConfirmationPerson") ? fromData.getString("ConfirmationPerson") : ""); //确认人
        map.put("Remark", fromData.containsKey("Remark") ? fromData.getString("Remark") : ""); //备注
        map.put("ProjectDevelopmentOrder", fromData.containsKey("ProjectDevelopmentOrder") ? fromData.getString("ProjectDevelopmentOrder") : ""); //项目开发令
    }

    private void getRemoteChangeIssueParams(EntityBaseVO baseVO, List<TyppmRemoteObject> remoteObjectList, JSONObject jsonParams, String isQueryRisk) {
        JSONObject subFolderFilterDTO = jsonParams.getJSONObject("subFolderFilterDTO");
        subFolderFilterDTO.put("containerOid", baseVO.getOid());
        subFolderFilterDTO.put("containerOtype", baseVO.getOtype());
        JSONObject searchCondition = subFolderFilterDTO.getJSONObject("searchCondition");
        List<String> issueOidList = remoteObjectList.stream().map(TyppmRemoteObject::getRemoteObjectId).collect(Collectors.toList());
        RemoteFolderSearchObjectList searchObjectList = new RemoteFolderSearchObjectList();
        searchObjectList.setTypeInthid("ty.inteplm.change.CTyChangeIssue");
        List<List<String>> lists = CommonUtil.subObjectList(issueOidList, 999);
        List<String> riskBranchIdList = new ArrayList<>();
        for (List<String> list : lists) {
           List<String> riskList = rtProjectMapper.selectRiskByBranchIdS(list);
           if(CollUtil.isNotEmpty(riskList)){
               riskBranchIdList.addAll(riskList);
           }
        }
        if("0".equals(isQueryRisk)){
            List<String> filteredList = issueOidList.stream().filter(item -> !riskBranchIdList.contains(item)).collect(Collectors.toList());
            searchObjectList.setBranchidList(filteredList);
        }else {
            searchObjectList.setBranchidList(riskBranchIdList);
        }
        searchCondition.put("objectSearchList", CollUtil.newArrayList(JSON.toJSON(searchObjectList)));
    }

    private void checkChangeIssueIsExistAndDelete(EntityBaseVO baseVO, List<TyppmRemoteObject> remoteObjectList, Map<String, List<TyppmChangeIssueLinkDTO>> remoteObjectGroup) {
        if (!CollUtil.isEmpty(remoteObjectGroup)) {
            Map<String, List<TyppmRemoteObject>> collectMap = remoteObjectList.stream().collect(Collectors.groupingBy(TyppmRemoteObject::getRemoteObjectId));
            List<String> stringList = this.thirdRemoteRequestService.checkChangeIssueIsExist(new ArrayList<>(collectMap.keySet()));
            if (!CollUtil.isEmpty(stringList)) {
                List<String> deleteLinkList = new ArrayList<>();

                for (String branch : stringList) {
                    List<TyppmRemoteObject> remoteObjects = collectMap.get(branch);
                    remoteObjects.forEach((item) -> {
                        List<TyppmChangeIssueLinkDTO> linkDTOList = remoteObjectGroup.get(item.getOid());
                        linkDTOList.forEach((link) -> {
                            deleteLinkList.add(link.getLinkOid());
                        });
                        remoteObjectGroup.remove(item.getOid());
                    });
                    remoteObjectList.removeAll(remoteObjects);
                }

                DeleteIssueLinkDTO deleteIssueLinkDTO = new DeleteIssueLinkDTO();
                deleteIssueLinkDTO.setDeleteFlag(ProjectMngConstant.ISSUE_DELETE_PROJECT_LINK);
                deleteIssueLinkDTO.setProjectOid(baseVO.getOid());
                deleteIssueLinkDTO.setProjectOtype(baseVO.getOtype());
                deleteIssueLinkDTO.setOidList(deleteLinkList);
                this.typpmProjectChangeIssueLinkService.batchDeleteChangeIssueLink(deleteIssueLinkDTO);
            }
        }
    }

    public List<TyppmChangeIssueLinkDTO> listChangeIssueLinkDTOByProject(Map<String, Object> linkParams) {
        List<ChangeIssueLinkDTO> changeIssueLinkDTOS = this.projectChangeIssueLinkService.changeIssueLinkDTOByProject(linkParams);
        return this.baseConvert.copyToList(changeIssueLinkDTOS, TyppmChangeIssueLinkDTO.class);
    }

    private List<JSONObject> mergeRemoteLinkAndIssue(JSONObject issueJson, List<TyppmChangeIssueLinkDTO> linkDTOList) {
        List<JSONObject> result = new ArrayList<>();
        if (CollUtil.isEmpty(linkDTOList)) {
            return result;
        } else if (JSONUtil.isNull(issueJson)) {
            return result;
        } else {
            String issueStr = issueJson.toJSONString();
            Set<String> planNameList = new HashSet<>();
            List<String> jobNameList = new ArrayList<>();
            List<String> jobOidList = new ArrayList<>();
            List<String> linkOidList = new ArrayList<>();
            List<String> linkBOidList = new ArrayList<>();
            TyppmChangeIssueLinkDTO temp = new TyppmChangeIssueLinkDTO();
            linkDTOList.forEach((item) -> {
                if (CharSequenceUtil.isNotEmpty(item.getPlanName())) {
                    planNameList.add(item.getPlanName());
                }

                if (CharSequenceUtil.isNotEmpty(item.getJobName())) {
                    jobNameList.add(item.getJobName());
                }

                if (CharSequenceUtil.isNotEmpty(item.getJobOid())) {
                    jobOidList.add(item.getJobOid());
                }

                linkOidList.add(item.getLinkOid());
                linkBOidList.add(item.getLinkBOid());
            });
            this.baseConvert.copyProperties(linkDTOList.get(0), temp);
            temp.setPlanName(CollUtil.join(planNameList, ","));
            temp.setJobName(CollUtil.join(jobNameList, ","));
            temp.setJobOid(CollUtil.join(jobOidList, ","));
            temp.setLinkOid(CollUtil.join(linkOidList, ","));
            temp.setLinkBOid(CollUtil.join(linkBOidList, ","));
            String linkStr = JSON.toJSONString(temp);
            String s = linkStr.substring(0, linkStr.length() - 1) + "," + issueStr.substring(1);
            result.add(JSON.parseObject(s));
            return result;
        }
    }

    @Override
    public List<PlanDeliveryCompleteInfoDTO> getPlanDeliveryCompleteStatus(String projectOid) {
        // 查询项目计划任务信息
        TyppmPlan plan = this.planService.queryPlanDTOByParent(projectOid, TableTypeConstrant.TY_PROJECT_TYPE);
        JSONObject planActiveTree = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(plan.getOid(), plan.getOtype(), true);
        if (Objects.isNull(planActiveTree)) {
            throw RTErrorCodeEnum.PROJECT_PARAMETER_NOT_EXIST.getException("projectOid在系统中不存在");
        }
        JSONArray tasks = planActiveTree.getJSONArray("Tasks");
        if (CollUtil.isEmpty(tasks)) {
            throw RTErrorCodeEnum.PROJECT_PARAMETER_NOT_EXIST.getException("任务为空");
        }
        List<PlanDeliveryCompleteInfoDTO> result = new ArrayList<>();

        // 使用AtomicInteger作为序号计数器（引用类型，支持跨方法累加）
        AtomicInteger serialNumber = new AtomicInteger(0);

        for (int i = 0; i < tasks.size(); i++) {
            JSONObject firstPlanTaskJSON = tasks.getJSONObject(i);
            String stateName = firstPlanTaskJSON.getString("Name");
            // 传递AtomicInteger对象，内部会自动累加
            buildResultData(firstPlanTaskJSON, result, serialNumber, stateName);

            JSONArray secondPlan = firstPlanTaskJSON.getJSONArray("children");
            if (CollUtil.isNotEmpty(secondPlan)) {
                for (int j = 0; j < secondPlan.size(); j++) {
                    JSONObject secondPlanTaskJSON = secondPlan.getJSONObject(j);
                    buildResultData(secondPlanTaskJSON, result, serialNumber, stateName);
                }
            }
        }
        return result;
    }

    // 注意：serialNumber参数类型改为AtomicInteger
    private void buildResultData(JSONObject planTask, List<PlanDeliveryCompleteInfoDTO> result, AtomicInteger serialNumber, String stateName) {
        String jobOID = planTask.getString("jobOID");
        List<TyppmOutRuleDTO> outRuleDTOS = this.typpmOutRuleService.queryOutRuleByPage(jobOID, "", 1, 999).getList();

        if (CollUtil.isNotEmpty(outRuleDTOS)) {
            for (TyppmOutRuleDTO outRuleDTO : outRuleDTOS) {
                if(org.apache.commons.lang3.StringUtils.isBlank(outRuleDTO.getRuleDisplayName())){
                    continue;
                }
                // 自增并获取最新序号（线程安全的累加）
                int currentNum = serialNumber.incrementAndGet();
                PlanDeliveryCompleteInfoDTO dto = new PlanDeliveryCompleteInfoDTO();
                dto.setSerialNumber(currentNum);
                dto.setStageTaskName(stateName);
                dto.setTaskName(planTask.getString("Name"));
                dto.setExecutorRole(planTask.getString("executorGroupDisplayName"));
                dto.setOutputType(outRuleDTO.getRuletypeDisplayName());
                dto.setRule(outRuleDTO.getRuleDisplayName());
                dto.setCreator(outRuleDTO.getCretaeName()); // 注意：可能存在拼写错误（CretaeName → CreateName）
                dto.setCreateTime(String.valueOf(outRuleDTO.getCreatestamp()));
                List<RemoteObjDTO> delivers = outRuleDTO.getDeliverName();
                if (CollUtil.isNotEmpty(delivers)) {
                    dto.setArchiveStatus("已归档");
                    List<JSONObject> deliveryList = new ArrayList<>();
                    for (RemoteObjDTO deliver : delivers) {
                        String extPros = deliver.getExtPros();
                        deliveryList.add(JSONObject.parseObject(extPros));
                    }
                    dto.setDeliveryList(deliveryList);
                } else {
                    dto.setArchiveStatus("未归档");
                }
                result.add(dto);
            }
        }
//        else {
            // 无规则时也累加序号
//            int currentNum = serialNumber.incrementAndGet();
//            PlanDeliveryCompleteInfoDTO dto = new PlanDeliveryCompleteInfoDTO();
//            dto.setSerialNumber(currentNum);
//            dto.setStageTaskName(stateName);
//            dto.setTaskName(planTask.getString("Name"));
//            dto.setExecutorRole(planTask.getString("executorGroupDisplayName"));
//            result.add(dto);
//        }
    }

    @Override
    public JSONObject getProjectPlanCompareResult(String projectOid) {
        TyppmPlan plan = this.planService.queryPlanDTOByParent(projectOid, TableTypeConstrant.TY_PROJECT_TYPE);
        JSONObject planActiveTree = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(plan.getOid(), plan.getOtype(), true);
        if (Objects.isNull(planActiveTree)) {
            throw SystemErrorCodeEnum.ENTITY_NONE.message("查询不到项目");
        }

        // 获取初始计划。
        Map<String, JSONObject> initialPlanActiveMap = new HashMap<>();
        List<TyppmBaselinePlan> baseline = this.baselinePlanService.queryBaselinePlanPageByParent(projectOid, TableTypeConstrant.TY_PROJECT_TYPE, 1, 999).getList();
        Optional<TyppmBaselinePlan> initialPlan = baseline.stream().filter(item -> "初始计划".equals(item.getBaselineName())).findFirst();
        if(initialPlan.isPresent()){
            TyppmBaselinePlan typpmBaselinePlan = initialPlan.get();
            Map<String, Object> params = new HashMap();
            params.put("rootOID", typpmBaselinePlan.getOid());
            params.put("rootOTYPE", typpmBaselinePlan.getOtype());
            params.put("markfordelete", 0L);
            List<TyppmActiveTreeDTO> activeTreeDTOList = this.baselinePlanActivityService.queryBaselinePlanActiveTreeByPlan(params);
            for (TyppmActiveTreeDTO typpmActiveTreeDTO : activeTreeDTOList) {
                StringBuffer name1 = new StringBuffer();
                convertPlanToMap(initialPlanActiveMap, typpmActiveTreeDTO, name1);
            }
        }
        if(CollUtil.isEmpty(initialPlanActiveMap)){
            throw SystemErrorCodeEnum.ENTITY_NONE.message("未成功获取到初始计划");
        }

        //获取当前计划。
        Map<String,JSONObject> currentPlanActiveMap = new HashMap<>();
        JSONArray currentPlanTasks = planActiveTree.getJSONArray("Tasks");
        StringBuffer name2 = new StringBuffer();
        for (int i = 0; i < currentPlanTasks.size(); i++) {
            convertPlanToMap(currentPlanActiveMap, currentPlanTasks.getJSONObject(i), name2);
        }

        JSONObject statePlanTask = currentPlanTasks.getJSONObject(0);
        String templateProjectOid = statePlanTask.getString("containerOID");

        // 获取模板计划。
        Map<String,JSONObject> templatePlanActiveMap = new HashMap<>();
        String templateOid = rtProjectMapper.selectProjectTemplateOidByProjectOid(projectOid);
        if(StringUtils.isNotEmpty(templateOid)){
            TyppmPlan templatePlan = this.planService.queryPlanDTOByParent(templateOid, TableTypeConstrant.TY_PROJECT_TYPE);
            JSONObject templatePlanActiveTree = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(templatePlan.getOid(), templatePlan.getOtype(), false);
            JSONArray templatePlanActiveTask = templatePlanActiveTree.getJSONArray("Tasks");
            StringBuffer name3 = new StringBuffer();
            for (int i = 0; i < templatePlanActiveTask.size(); i++) {
                convertPlanToMap(templatePlanActiveMap, templatePlanActiveTask.getJSONObject(i), name3);
            }
        }

        JSONObject compareResult = new JSONObject();
        if(CollUtil.isNotEmpty(templatePlanActiveMap)){
            // 模板计划与初始计划对比
            List<PlanActiveCompareDTO> result = comparePlanActiveTask2(initialPlanActiveMap,templatePlanActiveMap);
            compareResult.put("templateCompare",result);
        }
        if(CollUtil.isNotEmpty(currentPlanActiveMap)){
            // 当前计划与初始计划对比
            List<PlanActiveCompareDTO> result = comparePlanActiveTask2(initialPlanActiveMap,currentPlanActiveMap);
            compareResult.put("currentCompare",result);
        }
        return compareResult;
    }

    private List<PlanActiveCompareDTO> comparePlanActiveTask(Map<String, JSONObject> initialPlanActiveMap,
                                                             Map<String, JSONObject> templatePlanActiveMap) {
        List<PlanActiveCompareDTO> result = new ArrayList<>();
        for (Map.Entry<String, JSONObject> entry : initialPlanActiveMap.entrySet()) {
            JSONObject plan = entry.getValue();
            String key = entry.getKey();
            PlanActiveCompareDTO dto = new PlanActiveCompareDTO();
            if (templatePlanActiveMap.containsKey(key)) {
                dto.setInitialPlanName(plan.getString("name"));
                dto.setInitialPlanStartTime(plan.getString("startDate"));
                dto.setInitialPlanEndTime(plan.getString("finishDate"));
                dto.setInitialPredecessorTask(plan.getString("predecessorTask"));
                dto.setInitialDuration(convertTimeCompareResult(plan.getString("finishDate"), plan.getString("startDate")));
                dto.setInitialLevel(plan.getString("outlineLevel"));
                dto.setInitialPlanResponsiblePerson(plan.getString("executorName"));
                JSONObject templatePlan = templatePlanActiveMap.get(key);
                dto.setChgAfterLevel(templatePlan.getString("OutlineLevel"));
                dto.setChgAfterPlanStartTime(templatePlan.getString("Start"));
                dto.setChgAfterPlanEndTime(templatePlan.getString("Finish"));
                dto.setChgAfterPredecessorTask(templatePlan.getString("PredecessorTask"));
                dto.setChgAfterDuration(convertTimeCompareResult(templatePlan.getString("Finish"), templatePlan.getString("Start")));
                dto.setChgAfterResponsiblePerson(templatePlan.getString("executorName"));
                dto.setChgAfterPlanName(plan.getString("name"));
                int duration = Integer.parseInt(dto.getChgAfterDuration())-Integer.parseInt(dto.getInitialDuration());
                dto.setDurationComparison(String.valueOf(duration));
                result.add(dto);
            } else {
                // 删除处理计划
                dto.setInitialLevel(plan.getString("name"));
                dto.setInitialPlanStartTime(plan.getString("Start"));
                dto.setInitialPlanEndTime(plan.getString("Finish"));
                dto.setInitialPredecessorTask(plan.getString("PredecessorTask"));
                dto.setInitialDuration(convertTimeCompareResult(plan.getString("Finish"), plan.getString("Start")));
                dto.setInitialLevel(plan.getString("OutlineLevel"));
                dto.setInitialPlanResponsiblePerson(plan.getString("executorName"));
                result.add(dto);
            }
        }
        for (Map.Entry<String, JSONObject> entry : templatePlanActiveMap.entrySet()) {
            JSONObject templatePlan = entry.getValue();
            String key = entry.getKey();
            if (!initialPlanActiveMap.containsKey(key)) {
                // 添加删除计划
                PlanActiveCompareDTO dto = new PlanActiveCompareDTO();
                dto.setChgAfterLevel(templatePlan.getString("OutlineLevel"));
                dto.setChgAfterPlanStartTime(templatePlan.getString("Start"));
                dto.setChgAfterPlanEndTime(templatePlan.getString("Finish"));
                dto.setChgAfterPredecessorTask(templatePlan.getString("PredecessorTask"));
                dto.setChgAfterDuration(convertTimeCompareResult(templatePlan.getString("Finish"), templatePlan.getString("Start")));
                dto.setChgAfterResponsiblePerson(templatePlan.getString("executorName"));
                dto.setDurationComparison(convertTimeCompareResult(templatePlan.getString("Finish"), templatePlan.getString("Start")));
            }
        }
        return result;
    }

    private List<PlanActiveCompareDTO> comparePlanActiveTask2(Map<String, JSONObject> initialPlanActiveMap,
                                                             Map<String, JSONObject> templatePlanActiveMap) {
        List<PlanActiveCompareDTO> result = new ArrayList<>();
        for(Map.Entry<String, JSONObject> entry : initialPlanActiveMap.entrySet()){
            String key = entry.getKey();
            JSONObject value = entry.getValue();
            //初始模板存在    比对模板也存在   比对具体的数据
            PlanActiveCompareDTO dto = new PlanActiveCompareDTO();
            if(templatePlanActiveMap.containsKey(key)){
                String str1 = "";
                String str2 = "";
                String name = value.getString("name");
                String startDate = value.getString("startDate");
                String finishDate = value.getString("finishDate");
                String predecessorTask = value.getString("predecessorTask");
                String timeDuration = convertTimeCompareResult(value.getString("finishDate"), value.getString("startDate"));
                String outlineLevel = value.getString("outlineLevel");
                String executorName = value.getString("executorName");
                str1 =  name + startDate + finishDate + predecessorTask + timeDuration + outlineLevel + executorName;
                JSONObject templateJsonObject = templatePlanActiveMap.get(key);
                String templateName = templateJsonObject.getString("name");
                String templateStartDate = templateJsonObject.getString("startDate");
                String templateFinishDate = templateJsonObject.getString("finishDate");
                String templatePredecessorTask = templateJsonObject.getString("predecessorTask");
                String templateTimeDuration = convertTimeCompareResult(templateJsonObject.getString("finishDate"), templateJsonObject.getString("startDate"));
                String templateOutlineLevel = templateJsonObject.getString("outlineLevel");
                String templateExecutorName = templateJsonObject.getString("executorName");
                str2 = templateName + templateStartDate + templateFinishDate + templatePredecessorTask + templateTimeDuration + templateOutlineLevel + templateExecutorName;
                if(!str1.equals(str2)){
                    dto.setInitialLevel(outlineLevel);
                    dto.setInitialPlanName(name);
                    dto.setInitialPlanStartTime(startDate);
                    dto.setInitialPlanEndTime(finishDate);
                    dto.setInitialPredecessorTask(predecessorTask);
                    dto.setInitialDuration(timeDuration);
                    dto.setInitialPlanResponsiblePerson(executorName);
                    dto.setChgAfterLevel(templateOutlineLevel);
                    dto.setChgAfterPlanStartTime(templateStartDate);
                    dto.setChgAfterPlanEndTime(templateFinishDate);
                    dto.setChgAfterPredecessorTask(templatePredecessorTask);
                    dto.setChgAfterDuration(templateTimeDuration);
                    dto.setChgAfterResponsiblePerson(templateExecutorName);
                    dto.setChgAfterPlanName(templateName);
                    int duration = Integer.parseInt(dto.getChgAfterDuration())-Integer.parseInt(dto.getInitialDuration());
                    dto.setDurationComparison(String.valueOf(duration));
                    result.add(dto);
                }
            }else { // 初始模板中存在   比对的模板不存在
                dto.setInitialLevel(value.getString("name"));
                dto.setInitialPlanStartTime(value.getString("Start"));
                dto.setInitialPlanEndTime(value.getString("Finish"));
                dto.setInitialPredecessorTask(value.getString("PredecessorTask"));
                dto.setInitialDuration(convertTimeCompareResult(value.getString("Finish"), value.getString("Start")));
                dto.setInitialLevel(value.getString("OutlineLevel"));
                dto.setInitialPlanResponsiblePerson(value.getString("executorName"));
                result.add(dto);
            }
        }

        for(Map.Entry<String, JSONObject> entry : templatePlanActiveMap.entrySet()){
            String key = entry.getKey();
            JSONObject templatePlan = entry.getValue();
            //比对模板存在  初始模板不存在
            if(!initialPlanActiveMap.containsKey(key)){
                PlanActiveCompareDTO dto = new PlanActiveCompareDTO();
                dto.setChgAfterLevel(templatePlan.getString("OutlineLevel"));
                dto.setChgAfterPlanStartTime(templatePlan.getString("Start"));
                dto.setChgAfterPlanEndTime(templatePlan.getString("Finish"));
                dto.setChgAfterPredecessorTask(templatePlan.getString("PredecessorTask"));
                dto.setChgAfterDuration(convertTimeCompareResult(templatePlan.getString("Finish"), templatePlan.getString("Start")));
                dto.setChgAfterResponsiblePerson(templatePlan.getString("executorName"));
                dto.setDurationComparison(convertTimeCompareResult(templatePlan.getString("Finish"), templatePlan.getString("Start")));
                result.add(dto);
            }
        }
        return result;
    }

    private String convertTimeCompareResult(String finishTimeStr, String startTimeStr) {
        if(org.apache.commons.lang3.StringUtils.isBlank(finishTimeStr) || org.apache.commons.lang3.StringUtils.isBlank(startTimeStr)){
            return "1";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        LocalDateTime finishTime = LocalDateTime.parse(finishTimeStr, formatter);
        LocalDateTime startTime = LocalDateTime.parse(startTimeStr, formatter);

        // 计算总秒数差值
        long totalSeconds = ChronoUnit.SECONDS.between(startTime, finishTime);

        // 转换为天数并四舍五入
        double totalDays = totalSeconds / 86400.0;
        long roundedDays = Math.round(totalDays);
        return String.valueOf(roundedDays);
    }
    private void convertPlanToMap(Map<String, JSONObject> result, JSONObject currentPlan, StringBuffer parentName) {
        // 2. 拼接当前节点的完整 Key：父 Key + 分隔符（父非空时） + 当前节点名称
        StringBuffer currentKey = new StringBuffer();
        if (parentName != null && parentName.length() > 0) {
            currentKey.append(parentName).append("@@@;;;"); // 父Key非空才加分隔符
        }
        currentKey.append(currentPlan.getString("Name"));

        // 3. 存入Map（Key为拼接后的完整名称，Value为当前节点JSON）
        result.put(currentKey.toString(), currentPlan);

        // 4. 递归处理子节点（传入当前节点的完整Key作为子节点的父Key）
        JSONArray children = currentPlan.getJSONArray("children");
        if (CollUtil.isNotEmpty(children)) { // 先判断子节点非空，再循环（原代码判断位置有误）
            for (int i = 0; i < children.size(); i++) {
                JSONObject childPlan = children.getJSONObject(i);
                // 传入当前节点的完整Key，子节点会拼接为「当前Key@@@;;;子名称」
                convertPlanToMap(result, childPlan, currentKey);
            }
        }
    }

    private void convertPlanToMap(Map<String, JSONObject> result, TyppmActiveTreeDTO currentPlanTask,
                                  StringBuffer parentName) {
        // 1. 拼接当前节点的 Key：父节点名称 + 分隔符（父非空时） + 当前节点名称
        StringBuffer currentKey = new StringBuffer();
        if (parentName != null && parentName.length() > 0) {
            currentKey.append(parentName).append("@@@;;;"); // 父名称非空时，先加分隔符
        }
        currentKey.append(currentPlanTask.getName()); // 追加当前节点名称

        // 2. 存入 Map（Key 为拼接后的字符串，Value 为当前节点的 JSON）
        result.put(currentKey.toString(), (JSONObject) JSON.toJSON(currentPlanTask));

        // 3. 递归处理子节点（传入当前节点的 Key 作为子节点的父名称）
        List<TyppmActiveTreeDTO> children = currentPlanTask.getChildren();
        if (CollUtil.isNotEmpty(children)) {
            for (TyppmActiveTreeDTO childTask : children) {
                // 传入当前节点的 Key 作为子节点的父名称，子节点会拼接为「当前Key@@@;;;子名称」
                convertPlanToMap(result, childTask, currentKey);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCriticalTaskState() {
        List<ProjectEntityVO> list = this.searchAllProject("", "",1, 9999).getList();
        Map<BigInteger, SaveObjIBADTO> paramList = new HashMap<>();
        LocalDateTime currentDateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String currentDate = currentDateTime.format(formatter);
        for (ProjectEntityVO projectEntityVO : list) {
            TyppmPlan plan = this.planService.queryPlanDTOByParent(String.valueOf(projectEntityVO.getOid()), TableTypeConstrant.TY_PROJECT_TYPE);

            JSONObject projectPlanTree = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(plan.getOid(), plan.getOtype(), false);

            JSONArray tasks = projectPlanTree.getJSONArray("Tasks");
            Map<String, JSONObject> treeMap = new HashMap<>();
            planTreeConvertToMap(treeMap,tasks);
            LocalDateTime finishDate = plan.getFinishDate();
            String planFinishDate = finishDate.format(formatter);

            // 判断计划是否延期
            if(checkIfPostpone(currentDate,planFinishDate)){
                dealSaveAttrDTO(paramList,projectEntityVO.getOid(),projectEntityVO.getOtype(),"延期");
                // 如果计划延期，所有未完成的关键计划都延期
                dealChildPlan(paramList, tasks, currentDate, treeMap);
                continue;
            }

            for (int i = 0; i < tasks.size(); i++) {
                JSONObject stageTask = tasks.getJSONObject(i);
                String stageTaskFinishDate = stageTask.getString("Finish");
                String critical = stageTask.getString("Critical");
                if("1".equals(critical)){
                    // 关键阶段, 判断关键阶段是否延期。
                    if(checkIfPostpone(currentDate,stageTaskFinishDate)){
                        dealSaveAttrDTO(paramList,stageTask.getBigInteger("jobOID"),stageTask.getString("jobOTYPE"),"延期");
                        // 关键阶段，关键阶段下的子任务都延期。
                        dealChildPlan(paramList,stageTask.getJSONArray("children"), currentDate, treeMap);
                    }
                }else {
                    dealChildPlan(paramList,stageTask.getJSONArray("children"), currentDate, treeMap);
                }
            }
        }
        if(CollUtil.isNotEmpty(paramList)){
            typlmAttributeValueService.batchSaveIBAByObjList(ListUtil.toList(paramList.values()), false);
        }
    }

    private void planTreeConvertToMap(Map<String, JSONObject> treeMap, JSONArray tasks) {
        if (CollUtil.isNotEmpty(tasks)) {
            for (int i = 0; i < tasks.size(); i++) {
                JSONObject task = tasks.getJSONObject(i);
                treeMap.put(task.getString("id"), task);
                JSONArray children = task.getJSONArray("children");
                planTreeConvertToMap(treeMap,children);
            }
        }
    }

    private void dealChildPlan(Map<BigInteger, SaveObjIBADTO> paramList, JSONArray children, String currentDate, Map<String, JSONObject> treeMap) {
        if(CollUtil.isNotEmpty(children)){
            for (int i = 0; i < children.size(); i++) {
                JSONObject task = children.getJSONObject(i);
                String critical = task.getString("Critical");
                String taskFinishDate = task.getString("Finish");
                if("1".equals(critical)) {
                    if (checkIfPostpone(currentDate,taskFinishDate)) {
                        JSONObject ibaAttribute = task.getJSONObject("ibaAttribute");
                        if (ibaAttribute.containsKey("ExecutionStatus") && CollUtil.isNotEmpty(
                                ibaAttribute.getJSONArray("ExecutionStatus"))
                                && org.apache.commons.lang3.StringUtils.isNotBlank(
                                ibaAttribute.getJSONArray("ExecutionStatus").getString(0))) {
                            String executionStatus = ibaAttribute.getJSONArray("ExecutionStatus").getString(0);
                            if ("正常".equals(executionStatus)) {
                                dealSaveAttrDTO(paramList, task.getBigInteger("jobOID"), task.getString("jobOTYPE"),
                                                "延期");
                                // 向上递归处理父任务预延期
                                dealParentJob(treeMap, paramList, task);
                            }
                        }
                    }
                }

                //递归处理子节点
                dealChildPlan(paramList,task.getJSONArray("children"), currentDate, treeMap);
            }
        }
    }

    private void dealParentJob(Map<String, JSONObject> treeMap, Map<BigInteger, SaveObjIBADTO> paramList,JSONObject Task) {
        String parentId = Task.getString("parentId");
        if (treeMap.containsKey(parentId)) {
            JSONObject parentTask = treeMap.get(parentId);
            String critical = parentTask.getString("Critical");
            if ("1".equals(critical)) {
                dealSaveAttrDTO(paramList, parentTask.getBigInteger("jobOID"),parentTask.getString("jobOTYPE"),"预延期");
                dealParentJob(treeMap, paramList, parentTask);
            }
        }
    }

    private boolean checkIfPostpone(String currentDate, String stageTaskFinishDate) {
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate currentLocalDate = LocalDate.parse(currentDate, dateFormatter);
        String finishDateOnly = stageTaskFinishDate.substring(0, 10);
        LocalDate finishLocalDate = LocalDate.parse(finishDateOnly, dateFormatter);
        return currentLocalDate.isAfter(finishLocalDate);
    }

    private void dealSaveAttrDTO(Map<BigInteger, SaveObjIBADTO> result, BigInteger oid, String otype, String value) {
        if(result.containsKey(oid)){
            return;
        }
        SaveObjIBADTO saveObjIBADTO = new SaveObjIBADTO();
        CTyEntityBaseDO cTyEntityBaseDO = new CTyEntityBaseDO();
        cTyEntityBaseDO.setOid(oid);
        cTyEntityBaseDO.setOtype(otype);

        IbaDTO iba = new IbaDTO();
        iba.setKey("ExecutionStatus");
        iba.setValue(value);
        saveObjIBADTO.setCTyEntityBaseDO(cTyEntityBaseDO);
        saveObjIBADTO.setObjIbaList(Collections.singletonList(iba));

        result.put(oid,saveObjIBADTO);
    }

    @Override
    public JSONObject getProjectPlanByProjectOid(String projectOid) {
        TyppmPlan plan = this.typpmPlanService.queryPlanDTOByParent(projectOid, TableTypeConstrant.TY_PROJECT_TYPE);
        JSONObject jsonObject = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(
                plan.getOid(), plan.getOtype(), false
        );
        removeCompletedNodesRecursively(jsonObject);

        return jsonObject;
    }

    /**
     * 递归移除所有层级中“执行状态=完成”的节点
     * @param node 待处理的节点（根节点或子节点）
     */
    private void removeCompletedNodesRecursively(JSONObject node) {
        if (node == null) {
            return;
        }

        // 处理当前节点的直接子任务（一级Tasks或子节点的children）
        JSONArray tasks = node.getJSONArray("Tasks");
        if (CollUtil.isEmpty(tasks)) {
            // 若没有Tasks，尝试读取children（适配子节点的结构）
            tasks = node.getJSONArray("children");
            if (CollUtil.isEmpty(tasks)) {
                return;
            }
        }

        // 遍历任务，移除“完成”状态的节点，并递归处理子节点
        for (int i = tasks.size() - 1; i >= 0; i--) {
            JSONObject task = tasks.getJSONObject(i);
            if (task == null) {
                tasks.remove(i);
                continue;
            }

            // 递归处理当前任务的子节点（children）
            removeCompletedNodesRecursively(task);

            // 判断当前任务是否需要移除（执行状态=完成）
            if (isTaskCompleted(task)) {
                tasks.remove(i);
            }
        }
    }

    /**
     * 判断单个任务是否为“完成”状态
     * @param task 任务节点
     * @return true=已完成，false=未完成
     */
    private boolean isTaskCompleted(JSONObject task) {
        if (task == null) {
            return false;
        }

        // 获取ibaAttribute中的ExecutionStatus
        JSONObject ibaAttribute = task.getJSONObject("ibaAttribute");
        if (ibaAttribute == null) {
            return false;
        }

        JSONArray executionStatusArr = ibaAttribute.getJSONArray("ExecutionStatus");
        // 条件：ExecutionStatus数组非空，且第一个元素为“完成”
        return CollectionUtils.isNotEmpty(executionStatusArr)
                && "完成".equals(executionStatusArr.getString(0));
    }
}
